import functools
import time

import logzero
import stackprinter
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtWidgets import (
    QCheckBox,
    QDoubleSpinBox,
    QGridLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QMessageBox,
    QPushButton,
    QRadioButton,
    QWidget,
)

from helpers.mengy_bytes_helper import bytes2Int, getChineseStringLength
import mengy_spac800
import mengy_spac800_bt
from qt_custom_widget.qt_helper import addTooltipInSpinBox, dealGroubBpxDoubleClick
from qt_custom_widget.qt_ScrollMessageBox import ScrollMessageBox

logger = logzero.setup_logger("tabs.8.factory")


class Tab_8_Factory(QWidget):
    def __init__(self, serialObj, serRecvDir, sendQueuePack, logger=logger):
        super().__init__()
        self.serialObj = serialObj
        self.serRecvDir = serRecvDir
        self.sendQueuePack = sendQueuePack
        self.logger = logger
        self.initUI()
        self.refreshTime = time.time()
        self.logger.info("this is tab8 factory logger")
        self.setObjectName("tab8")
        layout = QGridLayout(self)
        layout.addWidget(self.emulateACGroupBox, 0, 0, 8, 2)  # 交流旁路
        layout.addWidget(self.emulateDoGroupBox, 0, 2, 2, 1)  # 开出调试
        layout.addWidget(self.specialCtrlGroupBox, 2, 2, 6, 1)  # 厂家控制
        layout.addWidget(self.prtUIGroupBox_Adj, 0, 3, 8, 3)  # 保护监视
        layout.addWidget(self.lowRangeGroupBox, 0, 6, 8, 2)  # 低量程校正参数
        layout.addWidget(self.hightRangeGroupBox, 0, 8, 8, 2)  # 高量程校正参数

    def initUI(self):
        """初始化显示构件"""
        self.createEmulateDoGroupBox()  # 开出调试
        self.createEmulateACGroupBox()  # 交流旁路
        self.createSpecialCtrlGroupBox()  # 厂家控制
        self.createAdjUIGroupBox()  # 保护监视
        self.createAdjustParaGroupBox()  # 校正参数

    def createEmulateACGroupBox(self):
        self.emulateACGroupBox = QGroupBox("交流旁路")
        layout = QGridLayout(self.emulateACGroupBox)
        self.bypassEnableWs = []
        self.bypassValueWs = []
        for i, conf in enumerate(mengy_spac800.BYPASS_CONFS):
            if conf.checked is True:
                cb = QCheckBox(conf.name)
                self.bypassEnableWs.append(cb)
                layout.addWidget(cb, i, 0)
            else:
                layout.addWidget(QLabel(conf.name), i, 0)
            dsb = QDoubleSpinBox()
            dsb.setSuffix(conf.unit)
            dsb.setDecimals(conf.decimal)
            dsb.setSingleStep(conf.singleStep)
            addTooltipInSpinBox(dsb, conf.range)
            self.bypassValueWs.append(dsb)
            layout.addWidget(dsb, i, 1)
        self.emulateACWriteBtn = QPushButton("写入")
        layout.addWidget(self.emulateACWriteBtn, len(mengy_spac800.BYPASS_CONFS), 0, 1, 2)
        self.emulateACWriteBtn.clicked.connect(self.sendBypassConf)
        self.emulateACGroupBox.mouseDoubleClickEvent = functools.partial(dealGroubBpxDoubleClick, self.bypassEnableWs)
        self.emulateACGroupBox.setToolTip("双击左键反选\n双击右键全选\n双击中键全不选")
        self.emulateACGroupBox.setToolTipDuration(5000)

    def sendBypassEnableBits(self):
        """send bypass enable bits"""
        enBits = 0
        for i, w in enumerate(self.bypassEnableWs):
            if w.isChecked():
                enBits += 1 << i
        self.sendQueuePack(funCode=0x10, start=0x0E90, data=(enBits,))

    def sendBypassValue(self, num):
        """send single bypass value"""
        sender = self.bypassValueWs[num]
        data = int(round(sender.value() * (10 ** sender.decimals())))
        self.sendQueuePack(funCode=0x10, start=0x0E91 + num, data=(data,))

    def sendBypassValueWhole(self):
        """send whole bypass values"""
        data = [int(round(sender.value() * (10 ** sender.decimals()))) for sender in self.bypassValueWs]
        self.sendQueuePack(funCode=0x10, start=0x0E91, data=data)

    def sendBypassConf(self):
        """send bypass enable bits and values"""
        self.sendBypassEnableBits()
        self.sendBypassValueWhole()

    def sendDOEmulateSingle(self, idx):
        w = self.emulateDoWs[idx]
        if w.isChecked():
            value = 0xFF00
        else:
            value = 0x0000
        self.sendQueuePack(funCode=0x06, start=0x0E70 + idx, value=value)

    def dealDOEmulateCheckBox(self, v, idx):
        self.sendDOEmulateSingle(idx)

    def dealDOEmulateLabel(self, event=None, idx=0):
        if not self.emulateDoTipsLb.isEnabled():
            return
        d = self.doStatusWs_Adj[idx]
        if d.text() == "合":
            value = 0x0000
        else:
            value = 0xFF00
        self.sendQueuePack(funCode=0x06, start=0x0E70 + idx, value=value)

    def sendDOEmulateWhole(self):
        for i, w in enumerate(self.emulateDoWs):
            if w.isChecked():
                value = 0xFF00
            else:
                value = 0x0000
            self.sendQueuePack(funCode=0x06, start=0x0E70 + i, value=value)

    def createEmulateDoGroupBox(self):
        self.emulateDoGroupBox = QGroupBox("开出调试")
        layout = QGridLayout(self.emulateDoGroupBox)
        self.emulateDoWs = []
        self.doStatusWs_Adj = []
        for i in range(mengy_spac800.DO_NUM_WITHOUT_LED):
            doCheckBox = QCheckBox(str(i + 1))
            doCheckBox.stateChanged.connect(functools.partial(self.dealDOEmulateCheckBox, idx=i))
            self.emulateDoWs.append(doCheckBox)
            layout.addWidget(self.emulateDoWs[i], i // 2, (i % 2) * 2 + 1)
            lb = QLabel("  ")
            lb.mousePressEvent = functools.partial(self.dealDOEmulateLabel, idx=i)
            self.doStatusWs_Adj.append(lb)
            layout.addWidget(self.doStatusWs_Adj[i], i // 2, (i % 2) * 2)
            self.doStatusWs_Adj[i].setAutoFillBackground(True)
            self.doStatusWs_Adj[i].setAlignment(Qt.AlignCenter)
        self.emulateDoTipsLb = QLabel("已进入开出模式")
        self.emulateDoTipsLb.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.emulateDoTipsLb, i + 1, 0, 1, 4)
        self.emulateDoGroupBox.setToolTip("双击左键反选\n双击右键全选\n双击中键全不选")
        self.emulateDoGroupBox.setToolTipDuration(5000)
        self.emulateDoGroupBox.mouseDoubleClickEvent = functools.partial(dealGroubBpxDoubleClick, self.emulateDoWs)

    def createSpecialCtrlGroupBox(self):
        self.specialCtrlGroupBox = QGroupBox("厂家控制")
        layout = QGridLayout(self.specialCtrlGroupBox)
        self.adjustButtons = []

        for i, name in enumerate(mengy_spac800.AUTO_ADUJST_NAME):
            subGB = QGroupBox(name)
            subLayout = QHBoxLayout()
            self.adjustButtons.append(QPushButton("低量程"))
            self.adjustButtons.append(QPushButton("高量程"))
            subLayout.addWidget(self.adjustButtons[-2])
            subLayout.addWidget(self.adjustButtons[-1])
            subGB.setLayout(subLayout)
            layout.addWidget(subGB, i, 0)

        for b in self.adjustButtons:
            b.clicked.connect(self.dealTab7Button)

        self.resetDefaultButton = QPushButton("恢复厂家设置")
        self.resetDefaultButton.clicked.connect(self.dealTab7Button)
        layout.addWidget(self.resetDefaultButton, 4, 0)
        self.runModelGroup = QGroupBox("运行模式控制")
        subLayout = QGridLayout()
        self.radioDebug = QRadioButton("开出调试(保护退出)")
        self.radioDebug.clicked.connect(self.dealTab7Button)
        self.radioBypass = QRadioButton("交流旁路")
        self.radioBypass.clicked.connect(self.dealTab7Button)
        self.radioNormal = QRadioButton("正常")
        self.radioNormal.clicked.connect(self.dealTab7Button)
        self.radioNormal.setChecked(True)
        self.runModelRs = (self.radioDebug, self.radioBypass, self.radioNormal)
        subLayout.addWidget(self.radioNormal, 0, 0)
        subLayout.addWidget(self.radioBypass, 1, 0)
        subLayout.addWidget(self.radioDebug, 2, 0)
        self.runModelGroup.setLayout(subLayout)
        layout.addWidget(self.runModelGroup, 5, 0)

    def adjustAddRow(self, layout, i, w):
        vl = QLabel("  ")
        tl = QLabel("  ")

        vvl = QLabel(
            "{:^{}s}: ".format(
                mengy_spac800.PRT_UI_CONFS[w].name, 7 - getChineseStringLength(mengy_spac800.PRT_UI_CONFS[w].name)
            )
        )
        vvl.setAlignment(Qt.AlignCenter)
        layout.addWidget(vvl, i, 0)
        vl.setAlignment(Qt.AlignCenter)
        tl.setAlignment(Qt.AlignCenter)
        layout.addWidget(vl, i, 1)
        ttl = QLabel("误差")
        ttl.setAlignment(Qt.AlignCenter)
        layout.addWidget(ttl, i, 2)
        layout.addWidget(tl, i, 3)
        return vl, tl

    def createAdjUIGroupBox(self):
        """校正用 保护监视界面初始化"""
        self.adjPrtStandardList = [
            100 * 3 ** 0.5,
            100 * 3 ** 0.5,
            100 * 3 ** 0.5,
            0,
            240,
            120,
            20,
            20,
            20,
            330,
            210,
            90,
            100 * 3 ** 0.5,
            4,
        ]
        self.adjPrtToleranceList = [0.5, 0.5, 0.5, 1, 1, 1, 0.5, 0.5, 0.5, 1, 1, 1, 0.5, 0.5]

        self.prtUIGroupBox_Adj = QGroupBox("保护测量")
        layout = QGridLayout(self.prtUIGroupBox_Adj)
        self.prtUI_Ws_Adj = []  # 测量值
        self.prtUI_Tolerance_Ws_Adj = []  # 误差值
        self.adjPrtStandardSPBs = []  # 测量比较的基准值
        self.adjPrtToleranceSPBs = []  # 最大允许误差

        for i in range(4):
            self.adjPrtStandardSPBs.append(QDoubleSpinBox())
            self.adjPrtToleranceSPBs.append(QDoubleSpinBox())
            self.adjPrtStandardSPBs[i].valueChanged.connect(self.adjStandardChange)
            self.adjPrtToleranceSPBs[i].valueChanged.connect(self.adjStandardChange)
            self.adjPrtStandardSPBs[i].setSuffix(("V", "A", "V", "A")[i])
            self.adjPrtToleranceSPBs[i].setSuffix("%")

        prtUgb = QGroupBox("电压量")
        uLayout = QGridLayout()
        for i, w in enumerate((0, 1, 2, 13, 14, 15)):
            vl, tl = self.adjustAddRow(uLayout, i, w)
            self.prtUI_Ws_Adj.append(vl)
            self.prtUI_Tolerance_Ws_Adj.append(tl)
        sspb = self.adjPrtStandardSPBs[0]
        addTooltipInSpinBox(sspb, (5.0, 40000.0))
        sspb.setDecimals(1)
        sspb.setValue(100.0)
        tspb = self.adjPrtToleranceSPBs[0]
        addTooltipInSpinBox(tspb, (0.0, 9.0))
        tspb.setDecimals(2)
        tspb.setSingleStep(0.05)
        tspb.setValue(0.5)
        standardLayout = QHBoxLayout()
        standardLayout.addWidget(QLabel("输入值"))
        standardLayout.addWidget(sspb)
        standardLayout.addWidget(QLabel("误差"))
        standardLayout.addWidget(tspb)
        standardLayout.addStretch(1)
        uLayout.addLayout(standardLayout, 6, 0, 1, 4)
        prtUgb.setLayout(uLayout)

        prtIgb = QGroupBox("电流量")
        iLayout = QGridLayout()
        for i, w in enumerate((3, 4, 5, 16, 17, 18)):
            vl, tl = self.adjustAddRow(iLayout, i, w)
            self.prtUI_Ws_Adj.append(vl)
            self.prtUI_Tolerance_Ws_Adj.append(tl)
        sspb = self.adjPrtStandardSPBs[1]
        addTooltipInSpinBox(sspb, (0.0, 5000.0))
        sspb.setDecimals(1)
        sspb.setValue(5.0)
        tspb = self.adjPrtToleranceSPBs[1]
        addTooltipInSpinBox(tspb, (0.0, 9.0))
        tspb.setDecimals(2)
        tspb.setSingleStep(0.05)
        tspb.setValue(0.5)
        standardLayout = QHBoxLayout()
        standardLayout.addWidget(QLabel("输入值"))
        standardLayout.addWidget(sspb)
        standardLayout.addWidget(QLabel("误差"))
        standardLayout.addWidget(tspb)
        standardLayout.addStretch(1)
        iLayout.addLayout(standardLayout, 6, 0, 1, 4)
        prtIgb.setLayout(iLayout)

        prtBgb = QGroupBox("不平衡量")
        bLayout = QGridLayout()
        for i, w in enumerate((6, 7)):
            vl, tl = self.adjustAddRow(bLayout, i, w)
            self.prtUI_Ws_Adj.append(vl)
            self.prtUI_Tolerance_Ws_Adj.append(tl)
        sspb = self.adjPrtStandardSPBs[2]
        addTooltipInSpinBox(sspb, (5.0, 40000.0))
        sspb.setDecimals(1)
        sspb.setValue(100.0)
        tspb = self.adjPrtToleranceSPBs[2]
        addTooltipInSpinBox(tspb, (0.0, 9.0))
        tspb.setDecimals(2)
        tspb.setSingleStep(0.05)
        tspb.setValue(0.5)
        standardLayout = QHBoxLayout()
        standardLayout.addWidget(QLabel("Ux输入值"))
        standardLayout.addWidget(sspb)
        standardLayout.addWidget(QLabel("误差"))
        standardLayout.addWidget(tspb)
        standardLayout.addStretch(1)
        bLayout.addLayout(standardLayout, 2, 0, 1, 4)
        sspb = self.adjPrtStandardSPBs[3]
        addTooltipInSpinBox(sspb, (0.0, 5000.0))
        sspb.setDecimals(1)
        sspb.setValue(5.0)
        tspb = self.adjPrtToleranceSPBs[3]
        addTooltipInSpinBox(tspb, (0.0, 9.0))
        tspb.setDecimals(2)
        tspb.setSingleStep(0.05)
        tspb.setValue(0.5)
        standardLayout = QHBoxLayout()
        standardLayout.addWidget(QLabel("Ix输入值"))
        standardLayout.addWidget(sspb)
        standardLayout.addWidget(QLabel("误差"))
        standardLayout.addWidget(tspb)
        standardLayout.addStretch(1)
        bLayout.addLayout(standardLayout, 3, 0, 1, 4)
        prtBgb.setLayout(bLayout)

        layout.addWidget(prtUgb, 0, 0, 7, 1)
        layout.addWidget(prtIgb, 7, 0, 7, 1)
        layout.addWidget(prtBgb, 14, 0, 4, 1)

    def adjStandardChange(self):
        sender = self.sender()
        if sender in self.adjPrtStandardSPBs:
            for i in range(3):
                self.adjPrtStandardList[0 + i] = self.adjPrtStandardSPBs[0].value()
                self.adjPrtStandardList[6 + i] = self.adjPrtStandardSPBs[1].value()
            self.adjPrtStandardList[12] = self.adjPrtStandardSPBs[2].value()
            self.adjPrtStandardList[13] = self.adjPrtStandardSPBs[3].value()
        elif sender in self.adjPrtToleranceSPBs:
            for i in range(3):
                self.adjPrtToleranceList[0 + i] = self.adjPrtToleranceSPBs[0].value()
                self.adjPrtToleranceList[6 + i] = self.adjPrtToleranceSPBs[1].value()
            self.adjPrtToleranceList[12] = self.adjPrtToleranceSPBs[2].value()
            self.adjPrtToleranceList[13] = self.adjPrtToleranceSPBs[3].value()
        self.updatePrtMea()

    def createAdjustParaGroupBox(self):
        self.adjustValueOverRangeFlags = [False, False]
        self.adjustValueWs = []
        self.lowRangeGroupBox = QGroupBox("低量程校正系数")
        lowLayout = QGridLayout(self.lowRangeGroupBox)
        for i, conf in enumerate(mengy_spac800.ADJUST_VALUE_CONFS):
            lowLayout.addWidget(QLabel(conf.name), i, 0)
            dsb = QDoubleSpinBox()
            addTooltipInSpinBox(dsb, conf.range)
            dsb.setDecimals(conf.decimal)
            dsb.setSingleStep(conf.singleStep)
            self.adjustValueWs.append(dsb)
            lowLayout.addWidget(dsb, i, 1)
        buttonLayout = QHBoxLayout()
        self.lowAdjReadButton = QPushButton("读取")
        self.lowAdjReadButton.clicked.connect(self.dealTab7Button)
        self.lowAdjWriteButton = QPushButton("写入")
        self.lowAdjWriteButton.clicked.connect(self.dealTab7Button)
        buttonLayout.addWidget(self.lowAdjReadButton)
        buttonLayout.addWidget(self.lowAdjWriteButton)
        lowLayout.addLayout(buttonLayout, 16, 0, 1, 2)

        self.hightRangeGroupBox = QGroupBox("高量程校正系数")
        hightLayout = QGridLayout(self.hightRangeGroupBox)
        for i, conf in enumerate(mengy_spac800.ADJUST_VALUE_CONFS):
            hightLayout.addWidget(QLabel(conf.name), i, 0)
            dsb = QDoubleSpinBox()
            addTooltipInSpinBox(dsb, conf.range)
            dsb.setDecimals(conf.decimal)
            dsb.setSingleStep(conf.singleStep)
            self.adjustValueWs.append(dsb)
            hightLayout.addWidget(dsb, i, 1)
        buttonLayout = QHBoxLayout()
        self.hightAdjReadButton = QPushButton("读取")
        self.hightAdjReadButton.clicked.connect(self.dealTab7Button)
        self.hightAdjWriteButton = QPushButton("写入")
        self.hightAdjWriteButton.clicked.connect(self.dealTab7Button)
        buttonLayout.addWidget(self.hightAdjReadButton)
        buttonLayout.addWidget(self.hightAdjWriteButton)
        hightLayout.addLayout(buttonLayout, 16, 0, 1, 2)
        return (self.lowRangeGroupBox, self.hightRangeGroupBox, self.adjustValueWs)

    def adjustVlaueGroupBoxRead(self, r=0):
        raw = tuple(self.serRecvDir.get(i) for i in range(0x0E40, 0x0E5F + 1))
        if any((i is None for i in raw)):
            return

        overRangeList = []
        data = tuple(bytes2Int(i) for i in raw)
        if r == 0:
            offset = 0
        else:
            offset = 16
        for i in range(16):
            wdgt = self.adjustValueWs[i + offset]
            showData = data[i + offset] / 10 ** wdgt.decimals()
            showName = mengy_spac800.ADJUST_VALUE_CONFS[i].name
            wdgt.setValue(showData)
            maximum = wdgt.maximum()
            minimum = wdgt.minimum()
            if showData > maximum or showData < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format(showName, showData, minimum, maximum))
                wdgt.setStyleSheet("color:#ff0000;")
            else:
                wdgt.setStyleSheet("")

        if len(overRangeList) > 0 and not self.adjustValueOverRangeFlags[r]:
            self.adjustValueOverRangeFlags[r] = True
            messageBox = ScrollMessageBox(msgList=overRangeList, width=250, parent=self)
            messageBox.setWindowTitle("校正参数-{} 超范围".format(("低量程", "高量程")[r]))
            messageBox.exec_()
            self.adjustValueOverRangeFlags[r] = False

    def adjustVlaueGroupBoxWrite(self, r=0):
        value = [int(round(w.value() * (10 ** w.decimals()))) for w in self.adjustValueWs]
        if r == 0:
            for w in self.adjustValueWs[:16]:
                w.setStyleSheet("color:#ff0000;")
            self.sendQueuePack(funCode=0x10, start=0x0E40, data=value[:16])
        else:
            for w in self.adjustValueWs[16:]:
                w.setStyleSheet("color:#ff0000;")
            self.sendQueuePack(funCode=0x10, start=0x0E50, data=value[16:])
        QTimer.singleShot(1500, lambda: self.adjustVlaueGroupBoxRead(r))

    def recoverFromAdj(self):
        """adjust finish recovery"""
        self.serialObj.timeout = 3600 / self.serialObj.baudrate
        for w in self.adjustButtons:
            if not w.isEnabled():
                w.setDisabled(False)

    def dealTab7Button(self):
        sender = self.sender()
        if sender in (self.lowAdjReadButton, self.hightAdjReadButton):  # 保护校正系数 读取
            idx = (self.lowAdjReadButton, self.hightAdjReadButton).index(sender)
            self.adjustVlaueGroupBoxRead(idx)
        elif sender in (self.lowAdjWriteButton, self.hightAdjWriteButton):  # 保护校正系数 写入
            idx = (self.lowAdjWriteButton, self.hightAdjWriteButton).index(sender)
            self.adjustVlaueGroupBoxWrite(idx)
        elif sender in self.adjustButtons:  # 自动校正 需要修改串口超时
            idx = self.adjustButtons.index(sender)
            self.serialObj.timeout = 10
            for w in self.adjustButtons:
                w.setDisabled(True)
            self.sendQueuePack(funCode=0x06, start=0x0E20, value=idx + 1)
            if idx == 0:  # 电流低量程
                self.adjPrtStandardSPBs[1].setValue(5)
            elif idx == 1:  # 电流高量程
                self.adjPrtStandardSPBs[1].setValue(20)
            elif idx == 2:  # 电压低量程
                self.adjPrtStandardSPBs[0].setValue(100)
            elif idx == 3:  # 电压高量程
                self.adjPrtStandardSPBs[0].setValue(100 * 3 ** 0.5)
            elif idx == 4:  # Ix低量程
                self.adjPrtStandardSPBs[3].setValue(1)
            elif idx == 5:  # Ix高量程
                self.adjPrtStandardSPBs[3].setValue(4)
            elif idx == 6:  # Ux低量程
                self.adjPrtStandardSPBs[2].setValue(100)
            elif idx == 7:  # Ux高量程
                self.adjPrtStandardSPBs[2].setValue(100 * 3 ** 0.5)
        elif sender in self.runModelRs:  # 运行模式切换
            idx = self.runModelRs.index(sender)
            choice = QMessageBox.question(self, "警告", "切换运行模式？", QMessageBox.Yes | QMessageBox.No)
            if choice == QMessageBox.Yes:
                self.sendQueuePack(funCode=0x06, start=0x0E22, value=idx + 1)
            else:
                self.dealDeviceWorkModel()
        elif sender is self.resetDefaultButton:  # 恢复出厂
            self.sendQueuePack(funCode=0x06, start=0x0E21, value=0xFF00)

    def updateDOStatus(self):
        if self.serRecvDir.get(0x0009) is None:
            return
        doData = bytes2Int(self.serRecvDir.get(0x0009))
        for i, w in enumerate(self.doStatusWs_Adj):
            if doData & (1 << i):
                w.setText("合")
                w.setStyleSheet("background-color:#ff0000;")
                self.emulateDoWs[i].blockSignals(True)
                self.emulateDoWs[i].setChecked(True)
                self.emulateDoWs[i].blockSignals(False)
            else:
                w.setText("分")
                w.setStyleSheet("background-color:#00ff00;")
                self.emulateDoWs[i].blockSignals(True)
                self.emulateDoWs[i].setChecked(False)
                self.emulateDoWs[i].blockSignals(False)

    def updatePrtMea(self):
        raw = tuple(self.serRecvDir.get(i) for i in range(0x000E, 0x0020 + 1))
        if any((i is None for i in raw)):
            return
        prtData = tuple(bytes2Int(i) for i in raw)
        for i, conf in enumerate(mengy_spac800.PRT_UI_CONFS):
            valueStr = "{:{}} {}".format(prtData[i] / conf.rate, conf.format, conf.unit)
            if conf.toleranceIndex >= 0:
                idx = conf.toleranceIndex
                self.prtUI_Ws_Adj[idx].setText(valueStr)
                standard = self.adjPrtStandardList[idx] * conf.rate
                if conf.unit != "°":
                    shift = 1
                    if i in range(0, 3):
                        shift = 3 ** 0.5
                    standard *= shift
                    tolerance = (prtData[i] - standard) / standard
                    self.prtUI_Tolerance_Ws_Adj[idx].setText("{:+.2%}".format(tolerance))
                    if abs(tolerance) * 100 < self.adjPrtToleranceList[idx]:
                        self.prtUI_Tolerance_Ws_Adj[idx].setStyleSheet("background-color:#00ff00;")
                    else:
                        self.prtUI_Tolerance_Ws_Adj[idx].setStyleSheet("background-color:#ff0000;")
                else:
                    tolerance = (prtData[i] - standard) / conf.rate
                    if tolerance > 180:
                        tolerance = tolerance - 360
                    elif tolerance < -180:
                        tolerance = tolerance + 360
                    self.prtUI_Tolerance_Ws_Adj[idx].setText("{:+.1f}°".format(tolerance))

    def dealDeviceWorkModel(self):
        """工作模式处理"""
        data_0x0004 = self.serRecvDir.get(0x0004)
        self.logger.info("data_0x0004 is {}".format(data_0x0004))
        if data_0x0004 is None:
            return
        runModel = (bytes2Int(data_0x0004) & (0b110)) >> 1  # 刷新工作模式
        if runModel == 0:  # 正常模式
            self.radioNormal.setChecked(True)
            self.emulateACWriteBtn.setDisabled(True)
            self.emulateACWriteBtn.setText("写入(需要交流旁路模式)")
            self.emulateDoTipsLb.setDisabled(True)
            for w in self.emulateDoWs:
                w.setDisabled(True)
            self.emulateDoTipsLb.setText("需要开出模式")
        elif runModel == 1:  # 旁路模式
            self.radioBypass.setChecked(True)
            self.emulateACWriteBtn.setDisabled(False)
            self.emulateACWriteBtn.setText("写入")
            self.emulateDoTipsLb.setDisabled(True)
            for w in self.emulateDoWs:
                w.setDisabled(True)
            self.emulateDoTipsLb.setText("需要开出模式")
            # 同步旁路值
            for i, w in enumerate(self.bypassValueWs):
                val = bytes2Int(self.serRecvDir.get(mengy_spac800.BYPASS_CONFS[i].modbusAddr)) / (10 ** w.decimals())
                if val != 0:
                    w.setValue(val)
        elif runModel == 2:  # 调试模式
            self.radioDebug.setChecked(True)
            self.emulateACWriteBtn.setDisabled(True)
            self.emulateACWriteBtn.setText("写入(需要交流旁路模式)")
            self.emulateDoTipsLb.setDisabled(False)
            self.emulateDoTipsLb.setText("已进入开出模式")
            for w in self.emulateDoWs:
                w.setDisabled(False)
            doData = bytes2Int(self.serRecvDir.get(0x0009))
            self.logger.info("do debug model doData {}".format(doData))

    def updateBySerialRecvBuff(self, changeAddrList):
        """数据缓存发生变化时需要刷新部分"""
        if 0x0004 in changeAddrList:
            self.dealDeviceWorkModel()  # 装置工作模式
            self.refreshTime = time.time()
        if 0x0009 in changeAddrList:
            self.updateDOStatus()
            self.refreshTime = time.time()
        if any(i in range(0x000E, 0x0020 + 1) for i in changeAddrList):
            self.updatePrtMea()
            self.refreshTime = time.time()
        if any(i in range(0x0E40, 0x0E4F + 1) for i in changeAddrList):
            self.adjustVlaueGroupBoxRead(0)  # 低量程校正
            self.refreshTime = time.time()
        if any(i in range(0x0E50, 0x0E5F + 1) for i in changeAddrList):
            self.adjustVlaueGroupBoxRead(1)  # 高量程校正
            self.refreshTime = time.time()

    def updateByTimer(self, timeout=1):
        if time.time() - self.refreshTime > timeout:
            self.updatePrtMea()  # 保护测量值
            self.dealDeviceWorkModel()  # 工作模式
            self.updateDOStatus()  # 开出状态
            self.adjustVlaueGroupBoxRead(0)  # 低量程校正
            self.adjustVlaueGroupBoxRead(1)  # 高量程校正
            self.refreshTime = time.time()

    def updateUnconditional(self):
        """无条件刷新界面显示部分"""
        pass

    def updateWhole(self):
        """更新全体显示构件"""
        self.updatePrtMea()  # 保护测量值
        self.dealDeviceWorkModel()  # 工作模式
        self.updateDOStatus()  # 开出状态
        self.adjustVlaueGroupBoxRead(0)  # 低量程校正
        self.adjustVlaueGroupBoxRead(1)  # 高量程校正
        self.refreshTime = time.time()


class Tab_8_Factory_BT(Tab_8_Factory):
    def createEmulateACGroupBox(self):
        self.emulateACGroupBox = QGroupBox("交流旁路")
        layout = QGridLayout(self.emulateACGroupBox)
        self.bypassEnableWs = []
        self.bypassValueWs = []
        for i, conf in enumerate(mengy_spac800_bt.BYPASS_CONFS):
            if conf.checked is True:
                cb = QCheckBox(conf.name)
                self.bypassEnableWs.append(cb)
                layout.addWidget(cb, i, 0)
            else:
                layout.addWidget(QLabel(conf.name), i, 0)
            dsb = QDoubleSpinBox()
            dsb.setSuffix(conf.unit)
            dsb.setDecimals(conf.decimal)
            dsb.setSingleStep(conf.singleStep)
            addTooltipInSpinBox(dsb, conf.range)
            self.bypassValueWs.append(dsb)
            layout.addWidget(dsb, i, 1)
        self.emulateACWriteBtn = QPushButton("写入")
        layout.addWidget(self.emulateACWriteBtn, len(mengy_spac800_bt.BYPASS_CONFS), 0, 1, 2)
        self.emulateACWriteBtn.clicked.connect(self.sendBypassConf)
        self.emulateACGroupBox.mouseDoubleClickEvent = functools.partial(dealGroubBpxDoubleClick, self.bypassEnableWs)
        self.emulateACGroupBox.setToolTip("双击左键反选\n双击右键全选\n双击中键全不选")
        self.emulateACGroupBox.setToolTipDuration(5000)

    def createSpecialCtrlGroupBox(self):
        self.specialCtrlGroupBox = QGroupBox("厂家控制")
        layout = QGridLayout(self.specialCtrlGroupBox)
        self.adjustButtons = []

        for i, name in enumerate(mengy_spac800_bt.AUTO_ADUJST_NAME):
            subGB = QGroupBox(name)
            subLayout = QHBoxLayout()
            self.adjustButtons.append(QPushButton("低量程"))
            self.adjustButtons.append(QPushButton("高量程"))
            subLayout.addWidget(self.adjustButtons[-2])
            subLayout.addWidget(self.adjustButtons[-1])
            subGB.setLayout(subLayout)
            layout.addWidget(subGB, i, 0)

        for b in self.adjustButtons:
            b.clicked.connect(self.dealTab7Button)

        self.resetDefaultButton = QPushButton("恢复厂家设置")
        self.resetDefaultButton.clicked.connect(self.dealTab7Button)
        layout.addWidget(self.resetDefaultButton, 4, 0)
        self.runModelGroup = QGroupBox("运行模式控制")
        subLayout = QGridLayout()
        self.radioDebug = QRadioButton("开出调试(保护退出)")
        self.radioDebug.clicked.connect(self.dealTab7Button)
        self.radioBypass = QRadioButton("交流旁路")
        self.radioBypass.clicked.connect(self.dealTab7Button)
        self.radioNormal = QRadioButton("正常")
        self.radioNormal.clicked.connect(self.dealTab7Button)
        self.radioNormal.setChecked(True)
        self.runModelRs = (self.radioDebug, self.radioBypass, self.radioNormal)
        subLayout.addWidget(self.radioNormal, 0, 0)
        subLayout.addWidget(self.radioBypass, 1, 0)
        subLayout.addWidget(self.radioDebug, 2, 0)
        self.runModelGroup.setLayout(subLayout)
        layout.addWidget(self.runModelGroup, 5, 0)

    def dealTab7Button(self):
        try:
            sender = self.sender()
            if sender in (self.lowAdjReadButton, self.hightAdjReadButton):  # 保护校正系数 读取
                idx = (self.lowAdjReadButton, self.hightAdjReadButton).index(sender)
                self.adjustVlaueGroupBoxRead(idx)
            elif sender in (self.lowAdjWriteButton, self.hightAdjWriteButton):  # 保护校正系数 写入
                idx = (self.lowAdjWriteButton, self.hightAdjWriteButton).index(sender)
                self.adjustVlaueGroupBoxWrite(idx)
            elif sender in self.adjustButtons:  # 自动校正 需要修改串口超时
                idx = self.adjustButtons.index(sender)
                self.serialObj.timeout = 10
                for w in self.adjustButtons:
                    w.setDisabled(True)
                self.sendQueuePack(funCode=0x06, start=0x0E20, value=idx + 3)
                if idx == 0:  # 电压低量程
                    self.adjPrtStandardSPBs[0].setValue(100)
                    self.adjPrtStandardSPBs[1].setValue(100)
                elif idx == 1:  # 电压高量程
                    self.adjPrtStandardSPBs[0].setValue(100 * 3 ** 0.5)
                    self.adjPrtStandardSPBs[1].setValue(100 * 3 ** 0.5)
                elif idx == 2:  # Ix低量程
                    self.adjPrtStandardSPBs[2].setValue(5)
                elif idx == 3:  # Ix高量程
                    self.adjPrtStandardSPBs[2].setValue(15)
                elif idx == 4:  # Ux低量程
                    self.adjPrtStandardSPBs[3].setValue(100)
                elif idx == 5:  # Ux高量程
                    self.adjPrtStandardSPBs[3].setValue(100 * 3 ** 0.5)
            elif sender in self.runModelRs:  # 运行模式切换
                idx = self.runModelRs.index(sender)
                choice = QMessageBox.question(self, "警告", "切换运行模式？", QMessageBox.Yes | QMessageBox.No)
                if choice == QMessageBox.Yes:
                    self.sendQueuePack(funCode=0x06, start=0x0E22, value=idx + 1)
                else:
                    self.dealDeviceWorkModel()
            elif sender is self.resetDefaultButton:  # 恢复出厂
                self.sendQueuePack(funCode=0x06, start=0x0E21, value=0xFF00)
        except Exception:
            self.logger.info("deal tab 7 button failed | {}".format(stackprinter.format()))

    def adjustAddRow(self, layout, i, w):
        vl = QLabel("  ")
        tl = QLabel("  ")

        vvl = QLabel(
            "{:^{}s}: ".format(
                mengy_spac800_bt.PRT_UI_CONFS[w].name, 7 - getChineseStringLength(mengy_spac800_bt.PRT_UI_CONFS[w].name)
            )
        )
        vvl.setAlignment(Qt.AlignCenter)
        layout.addWidget(vvl, i, 0)
        vl.setAlignment(Qt.AlignCenter)
        tl.setAlignment(Qt.AlignCenter)
        layout.addWidget(vl, i, 1)
        ttl = QLabel("误差")
        ttl.setAlignment(Qt.AlignCenter)
        layout.addWidget(ttl, i, 2)
        layout.addWidget(tl, i, 3)
        return vl, tl

    def createAdjUIGroupBox(self):
        """校正用 保护监视界面初始化"""
        self.adjPrtStandardList = [
            100 * 3 ** 0.5,
            100 * 3 ** 0.5,
            100 * 3 ** 0.5,
            0,
            240,
            120,
            100 * 3 ** 0.5,
            100 * 3 ** 0.5,
            100 * 3 ** 0.5,
            0,
            240,
            120,
            5,
            100 * 3 ** 0.5,
            5,
            100 * 3 ** 0.5,
        ]
        self.adjPrtToleranceList = [0.5, 0.5, 0.5, 1, 1, 1, 0.5, 0.5, 0.5, 1, 1, 1, 0.5, 0.5, 0.5, 0.5]

        self.prtUIGroupBox_Adj = QGroupBox("保护测量")
        layout = QGridLayout(self.prtUIGroupBox_Adj)
        self.prtUI_Ws_Adj = []  # 测量值
        self.prtUI_Tolerance_Ws_Adj = []  # 误差值
        self.adjPrtStandardSPBs = []  # 测量比较的基准值
        self.adjPrtToleranceSPBs = []  # 最大允许误差

        for i in range(4):
            self.adjPrtStandardSPBs.append(QDoubleSpinBox())
            self.adjPrtToleranceSPBs.append(QDoubleSpinBox())
            self.adjPrtStandardSPBs[i].valueChanged.connect(self.adjStandardChange)
            self.adjPrtToleranceSPBs[i].valueChanged.connect(self.adjStandardChange)
            self.adjPrtStandardSPBs[i].setSuffix(("V", "V", "A", "V")[i])
            self.adjPrtToleranceSPBs[i].setSuffix("%")

        prtUgb = QGroupBox("线电压1")
        uLayout = QGridLayout()
        for i, w in enumerate((0, 1, 2, 10, 11, 12)):
            vl, tl = self.adjustAddRow(uLayout, i, w)
            self.prtUI_Ws_Adj.append(vl)
            self.prtUI_Tolerance_Ws_Adj.append(tl)
        sspb = self.adjPrtStandardSPBs[0]
        addTooltipInSpinBox(sspb, (5.0, 40000.0))
        sspb.setDecimals(1)
        sspb.setValue(100.0)
        tspb = self.adjPrtToleranceSPBs[0]
        addTooltipInSpinBox(tspb, (0.0, 9.0))
        tspb.setDecimals(2)
        tspb.setSingleStep(0.05)
        tspb.setValue(0.5)
        standardLayout = QHBoxLayout()
        standardLayout.addWidget(QLabel("输入值"))
        standardLayout.addWidget(sspb)
        standardLayout.addWidget(QLabel("误差"))
        standardLayout.addWidget(tspb)
        standardLayout.addStretch(1)
        uLayout.addLayout(standardLayout, 6, 0, 1, 4)
        prtUgb.setLayout(uLayout)

        prtIgb = QGroupBox("线电压2")
        iLayout = QGridLayout()
        for i, w in enumerate((3, 4, 5, 13, 14, 15)):
            vl, tl = self.adjustAddRow(iLayout, i, w)
            self.prtUI_Ws_Adj.append(vl)
            self.prtUI_Tolerance_Ws_Adj.append(tl)
        sspb = self.adjPrtStandardSPBs[1]
        addTooltipInSpinBox(sspb, (5.0, 40000.0))
        sspb.setDecimals(1)
        sspb.setValue(100.0)
        tspb = self.adjPrtToleranceSPBs[1]
        addTooltipInSpinBox(tspb, (0.0, 9.0))
        tspb.setDecimals(2)
        tspb.setSingleStep(0.05)
        tspb.setValue(0.5)
        standardLayout = QHBoxLayout()
        standardLayout.addWidget(QLabel("输入值"))
        standardLayout.addWidget(sspb)
        standardLayout.addWidget(QLabel("误差"))
        standardLayout.addWidget(tspb)
        standardLayout.addStretch(1)
        iLayout.addLayout(standardLayout, 6, 0, 1, 4)
        prtIgb.setLayout(iLayout)

        prtBgb = QGroupBox("抽取量")
        bLayout = QGridLayout()
        for i, w in enumerate((6, 7, 8, 9)):
            vl, tl = self.adjustAddRow(bLayout, i, w)
            self.prtUI_Ws_Adj.append(vl)
            self.prtUI_Tolerance_Ws_Adj.append(tl)
        sspb = self.adjPrtStandardSPBs[2]
        addTooltipInSpinBox(sspb, (0.01, 5000.0))
        sspb.setDecimals(1)
        sspb.setValue(5.0)
        tspb = self.adjPrtToleranceSPBs[2]
        addTooltipInSpinBox(tspb, (0.0, 9.0))
        tspb.setDecimals(2)
        tspb.setSingleStep(0.05)
        tspb.setValue(0.5)
        standardLayout = QHBoxLayout()
        standardLayout.addWidget(QLabel("Ix输入值"))
        standardLayout.addWidget(sspb)
        standardLayout.addWidget(QLabel("误差"))
        standardLayout.addWidget(tspb)
        standardLayout.addStretch(1)
        bLayout.addLayout(standardLayout, 4, 0, 1, 4)
        sspb = self.adjPrtStandardSPBs[3]
        addTooltipInSpinBox(sspb, (5.0, 40000.0))
        sspb.setDecimals(1)
        sspb.setValue(100.0)
        tspb = self.adjPrtToleranceSPBs[3]
        addTooltipInSpinBox(tspb, (0.0, 9.0))
        tspb.setDecimals(2)
        tspb.setSingleStep(0.05)
        tspb.setValue(0.5)
        standardLayout = QHBoxLayout()
        standardLayout.addWidget(QLabel("Ux输入值"))
        standardLayout.addWidget(sspb)
        standardLayout.addWidget(QLabel("误差"))
        standardLayout.addWidget(tspb)
        standardLayout.addStretch(1)
        bLayout.addLayout(standardLayout, 5, 0, 1, 4)
        prtBgb.setLayout(bLayout)

        layout.addWidget(prtUgb, 0, 0, 6, 1)
        layout.addWidget(prtIgb, 6, 0, 6, 1)
        layout.addWidget(prtBgb, 12, 0, 4, 1)

    def adjStandardChange(self):
        sender = self.sender()
        if sender in self.adjPrtStandardSPBs:
            for i in range(3):
                self.adjPrtStandardList[0 + i] = self.adjPrtStandardSPBs[0].value()
                self.adjPrtStandardList[6 + i] = self.adjPrtStandardSPBs[1].value()
            self.adjPrtStandardList[12] = self.adjPrtStandardSPBs[2].value()
            self.adjPrtStandardList[14] = self.adjPrtStandardSPBs[2].value()
            self.adjPrtStandardList[13] = self.adjPrtStandardSPBs[3].value()
            self.adjPrtStandardList[15] = self.adjPrtStandardSPBs[3].value()
        elif sender in self.adjPrtToleranceSPBs:
            for i in range(3):
                self.adjPrtToleranceList[0 + i] = self.adjPrtToleranceSPBs[0].value()
                self.adjPrtToleranceList[6 + i] = self.adjPrtToleranceSPBs[1].value()
            self.adjPrtToleranceList[12] = self.adjPrtToleranceSPBs[2].value()
            self.adjPrtToleranceList[14] = self.adjPrtToleranceSPBs[2].value()
            self.adjPrtToleranceList[13] = self.adjPrtToleranceSPBs[3].value()
            self.adjPrtToleranceList[15] = self.adjPrtToleranceSPBs[3].value()
        self.updatePrtMea()

    def updatePrtMea(self):
        raw = tuple(self.serRecvDir.get(i) for i in range(0x000E, 0x001D + 1))
        if any((i is None for i in raw)):
            return
        prtData = tuple(bytes2Int(i) for i in raw)
        for i, conf in enumerate(mengy_spac800_bt.PRT_UI_CONFS):
            valueStr = "{:{}} {}".format(prtData[i] / conf.rate, conf.format, conf.unit)
            if conf.toleranceIndex >= 0:
                idx = conf.toleranceIndex
                self.prtUI_Ws_Adj[idx].setText(valueStr)
                standard = self.adjPrtStandardList[idx] * conf.rate
                if conf.unit != "°":
                    tolerance = (prtData[i] - standard) / standard
                    self.prtUI_Tolerance_Ws_Adj[idx].setText("{:+.2%}".format(tolerance))
                    if abs(tolerance) * 100 < self.adjPrtToleranceList[idx]:
                        self.prtUI_Tolerance_Ws_Adj[idx].setStyleSheet("background-color:#00ff00;")
                    else:
                        self.prtUI_Tolerance_Ws_Adj[idx].setStyleSheet("background-color:#ff0000;")
                else:
                    tolerance = (prtData[i] - standard) / conf.rate
                    if tolerance > 180:
                        tolerance = tolerance - 360
                    elif tolerance < -180:
                        tolerance = tolerance + 360
                    self.prtUI_Tolerance_Ws_Adj[idx].setText("{:+.1f}°".format(tolerance))

    def createAdjustParaGroupBox(self):
        self.adjustValueOverRangeFlags = [False, False]
        self.adjustValueWs = []
        self.lowRangeGroupBox = QGroupBox("低量程校正系数")
        lowLayout = QGridLayout(self.lowRangeGroupBox)
        for i, conf in enumerate(mengy_spac800_bt.ADJUST_VALUE_CONFS):
            lowLayout.addWidget(QLabel(conf.name), i, 0)
            dsb = QDoubleSpinBox()
            addTooltipInSpinBox(dsb, conf.range)
            dsb.setDecimals(conf.decimal)
            dsb.setSingleStep(conf.singleStep)
            self.adjustValueWs.append(dsb)
            lowLayout.addWidget(dsb, i, 1)
        buttonLayout = QHBoxLayout()
        self.lowAdjReadButton = QPushButton("读取")
        self.lowAdjReadButton.clicked.connect(self.dealTab7Button)
        self.lowAdjWriteButton = QPushButton("写入")
        self.lowAdjWriteButton.clicked.connect(self.dealTab7Button)
        buttonLayout.addWidget(self.lowAdjReadButton)
        buttonLayout.addWidget(self.lowAdjWriteButton)
        lowLayout.addLayout(buttonLayout, 16, 0, 1, 2)

        self.hightRangeGroupBox = QGroupBox("高量程校正系数")
        hightLayout = QGridLayout(self.hightRangeGroupBox)
        for i, conf in enumerate(mengy_spac800_bt.ADJUST_VALUE_CONFS):
            hightLayout.addWidget(QLabel(conf.name), i, 0)
            dsb = QDoubleSpinBox()
            addTooltipInSpinBox(dsb, conf.range)
            dsb.setDecimals(conf.decimal)
            dsb.setSingleStep(conf.singleStep)
            self.adjustValueWs.append(dsb)
            hightLayout.addWidget(dsb, i, 1)
        buttonLayout = QHBoxLayout()
        self.hightAdjReadButton = QPushButton("读取")
        self.hightAdjReadButton.clicked.connect(self.dealTab7Button)
        self.hightAdjWriteButton = QPushButton("写入")
        self.hightAdjWriteButton.clicked.connect(self.dealTab7Button)
        buttonLayout.addWidget(self.hightAdjReadButton)
        buttonLayout.addWidget(self.hightAdjWriteButton)
        hightLayout.addLayout(buttonLayout, 16, 0, 1, 2)
        return (self.lowRangeGroupBox, self.hightRangeGroupBox, self.adjustValueWs)

    def adjustVlaueGroupBoxRead(self, r=0):
        raw = tuple(self.serRecvDir.get(i) for i in range(0x0E40, 0x0E5F + 1))
        if any((i is None for i in raw)):
            return

        overRangeList = []
        data = tuple(bytes2Int(i) for i in raw)
        if r == 0:
            offset = 0
        else:
            offset = 16
        for i in range(16):
            wdgt = self.adjustValueWs[i + offset]
            showData = data[i + offset] / 10 ** wdgt.decimals()
            showName = mengy_spac800_bt.ADJUST_VALUE_CONFS[i].name
            wdgt.setValue(showData)
            maximum = wdgt.maximum()
            minimum = wdgt.minimum()
            if showData > maximum or showData < minimum:
                overRangeList.append("{} 读取值 {} 范围 {} ~ {}".format(showName, showData, minimum, maximum))
                wdgt.setStyleSheet("color:#ff0000;")
            else:
                wdgt.setStyleSheet("")

        if len(overRangeList) > 0 and not self.adjustValueOverRangeFlags[r]:
            self.adjustValueOverRangeFlags[r] = True
            messageBox = ScrollMessageBox(msgList=overRangeList, width=250, parent=self)
            messageBox.setWindowTitle("校正参数-{} 超范围".format(("低量程", "高量程")[r]))
            messageBox.exec_()
            self.adjustValueOverRangeFlags[r] = False

    def adjustVlaueGroupBoxWrite(self, r=0):
        value = [int(round(w.value() * (10 ** w.decimals()))) for w in self.adjustValueWs]
        if r == 0:
            for w in self.adjustValueWs[:16]:
                w.setStyleSheet("color:#ff0000;")
            self.sendQueuePack(funCode=0x10, start=0x0E40, data=value[:16])
        else:
            for w in self.adjustValueWs[16:]:
                w.setStyleSheet("color:#ff0000;")
            self.sendQueuePack(funCode=0x10, start=0x0E50, data=value[16:])
        QTimer.singleShot(1500, lambda: self.adjustVlaueGroupBoxRead(r))

    def dealDeviceWorkModel(self):
        """工作模式处理"""
        data_0x0004 = self.serRecvDir.get(0x0004)
        self.logger.info("data_0x0004 is {}".format(data_0x0004))
        if data_0x0004 is None:
            return
        runModel = (bytes2Int(data_0x0004) & (0b110)) >> 1  # 刷新工作模式
        if runModel == 0:  # 正常模式
            self.radioNormal.setChecked(True)
            self.emulateACWriteBtn.setDisabled(True)
            self.emulateACWriteBtn.setText("写入(需要交流旁路模式)")
            self.emulateDoTipsLb.setDisabled(True)
            for w in self.emulateDoWs:
                w.setDisabled(True)
            self.emulateDoTipsLb.setText("需要开出模式")
        elif runModel == 1:  # 旁路模式
            self.radioBypass.setChecked(True)
            self.emulateACWriteBtn.setDisabled(False)
            self.emulateACWriteBtn.setText("写入")
            self.emulateDoTipsLb.setDisabled(True)
            for w in self.emulateDoWs:
                w.setDisabled(True)
            self.emulateDoTipsLb.setText("需要开出模式")
            # 同步旁路值
            for i, w in enumerate(self.bypassValueWs):
                val = bytes2Int(self.serRecvDir.get(mengy_spac800_bt.BYPASS_CONFS[i].modbusAddr)) / (10 ** w.decimals())
                if val != 0:
                    w.setValue(val)
        elif runModel == 2:  # 调试模式
            self.radioDebug.setChecked(True)
            self.emulateACWriteBtn.setDisabled(True)
            self.emulateACWriteBtn.setText("写入(需要交流旁路模式)")
            self.emulateDoTipsLb.setDisabled(False)
            self.emulateDoTipsLb.setText("已进入开出模式")
            for w in self.emulateDoWs:
                w.setDisabled(False)
            doData = bytes2Int(self.serRecvDir.get(0x0009))
            self.logger.info("do debug model doData {}".format(doData))
